﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;   

namespace ICSharpCode.WinFormsUI.NGraphics
{
    /// <summary>
    /// 图片处理类
    /// </summary>
    public class ImageHandler
    {
        private static ImageCodecInfo GetEncoderInfo(string mimeType)
        {
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (int j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }

        private static Bitmap CreateGrayscaleImage(int width, int height)
        {
            // create new image
            Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
            // set palette to grayscale
            SetGrayscalePalette(bmp);
            // return new image
            return bmp;
        }

        private static void SetGrayscalePalette(Bitmap bitmap)
        {
            // check pixel format
            if (bitmap.PixelFormat != PixelFormat.Format8bppIndexed)
                throw new ArgumentException();

            // get palette
            ColorPalette cp = bitmap.Palette;
            // init palette
            for (int i = 0; i < 256; i++)
            {
                cp.Entries[i] = Color.FromArgb(i, i, i);
            }
            // set palette back
            bitmap.Palette = cp;
        }

        /// <summary>
        /// 变成黑白图
        /// </summary>
        /// <param name="bitmap">原始图</param>
        /// <param name="backgroundcolor">背景色</param>
        /// <returns></returns>
        public static Bitmap ConvertToColourless(Bitmap bitmap, Color backgroundcolor)
        {
            Bitmap tm = new Bitmap(bitmap.Width, bitmap.Height);
            using (Graphics graphic = Graphics.FromImage(tm))
            {
                graphic.Clear(backgroundcolor);
                graphic.DrawImage(bitmap, new Rectangle(new Point(0, 0), bitmap.Size));
            }
            Bitmap bm = new Bitmap(bitmap.Width, bitmap.Height);

            LockBitmap lbmp = new LockBitmap(tm);
            LockBitmap newlbmp = new LockBitmap(bm);
            lbmp.LockBits();
            newlbmp.LockBits();

            for (int y = 0; y < bm.Height; y++)
            {
                for (int x = 0; x < bm.Width; x++)
                {
                    Color c = lbmp.GetPixel(x, y);
                    int rgb = (int)(c.R * 0.299 + c.G * 0.587 + c.B * 0.114);
                    newlbmp.SetPixel(x, y, Color.FromArgb(rgb, rgb, rgb));
                }
            }

            lbmp.UnlockBits();
            newlbmp.UnlockBits();

            return bm;
        }

        /// <summary>
        /// 底片效果
        /// </summary>
        /// <param name="bitmap">原始图</param>
        /// <returns></returns>
        public static Bitmap ConvertToNegative(Bitmap bitmap, Color backgroundcolor)
        {
            Bitmap tm = new Bitmap(bitmap.Width, bitmap.Height);
            using (Graphics graphic = Graphics.FromImage(tm))
            {
                graphic.Clear(backgroundcolor);
                graphic.DrawImage(bitmap, new Rectangle(new Point(0, 0), bitmap.Size));
            }
            Bitmap bm = new Bitmap(bitmap.Width, bitmap.Height);

            LockBitmap lbmp = new LockBitmap(tm);
            LockBitmap newlbmp = new LockBitmap(bm);
            lbmp.LockBits();
            newlbmp.LockBits();

            for (int x = 0; x < bitmap.Width; x++)
            {
                for (int y = 0; y < bitmap.Height; y++)
                {
                    int r, g, b;
                    Color pixel = lbmp.GetPixel(x, y);
                    r = 255 - pixel.R;
                    g = 255 - pixel.G;
                    b = 255 - pixel.B;
                    newlbmp.SetPixel(x, y, Color.FromArgb(r, g, b));
                }
            }

            lbmp.UnlockBits();
            newlbmp.UnlockBits();

            return bm;
        }

        /// <summary>
        /// 浮雕效果
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="backgroundcolor"></param>
        /// <returns></returns>
        public static Bitmap ConvertToCameo(Bitmap bitmap, Color backgroundcolor)
        {
            Bitmap tm = new Bitmap(bitmap.Width, bitmap.Height);
            using (Graphics graphic = Graphics.FromImage(tm))
            {
                graphic.Clear(backgroundcolor);
                graphic.DrawImage(bitmap, new Rectangle(new Point(0, 0), bitmap.Size));
            }
            Bitmap bm = new Bitmap(bitmap.Width, bitmap.Height);

            LockBitmap lbmp = new LockBitmap(tm);
            LockBitmap newlbmp = new LockBitmap(bm);
            lbmp.LockBits();
            newlbmp.LockBits();

            Color pixel1, pixel2;
            for (int x = 0; x < bitmap.Width - 1; x++)
            {
                for (int y = 0; y < bitmap.Height - 1; y++)
                {
                    int r = 0, g = 0, b = 0;
                    pixel1 = lbmp.GetPixel(x, y);
                    pixel2 = lbmp.GetPixel(x + 1, y + 1);
                    r = Math.Abs(pixel1.R - pixel2.R + 128);
                    g = Math.Abs(pixel1.G - pixel2.G + 128);
                    b = Math.Abs(pixel1.B - pixel2.B + 128);
                    if (r > 255)
                        r = 255;
                    if (r < 0)
                        r = 0;
                    if (g > 255)
                        g = 255;
                    if (g < 0)
                        g = 0;
                    if (b > 255)
                        b = 255;
                    if (b < 0)
                        b = 0;
                    newlbmp.SetPixel(x, y, Color.FromArgb(r, g, b));
                }
            }

            lbmp.UnlockBits();
            newlbmp.UnlockBits();

            return bm;
        }

        /// <summary>
        /// 柔化效果
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="backgroundcolor"></param>
        /// <returns></returns>
        public static Bitmap ConvertToSoften(Bitmap bitmap, Color backgroundcolor)
        {
            Bitmap tm = new Bitmap(bitmap.Width, bitmap.Height);
            using (Graphics graphic = Graphics.FromImage(tm))
            {
                graphic.Clear(backgroundcolor);
                graphic.DrawImage(bitmap, new Rectangle(new Point(0, 0), bitmap.Size));
            }
            Bitmap bm = new Bitmap(bitmap.Width, bitmap.Height);

            LockBitmap lbmp = new LockBitmap(tm);
            LockBitmap newlbmp = new LockBitmap(bm);
            lbmp.LockBits();
            newlbmp.LockBits();

            Color pixel;
            //高斯模板
            int[] Gauss = { 1, 2, 1, 2, 4, 2, 1, 2, 1 };
            for (int x = 1; x < bitmap.Width - 1; x++)
            {
                for (int y = 1; y < bitmap.Height - 1; y++)
                {
                    int r = 0, g = 0, b = 0;
                    int Index = 0;
                    for (int col = -1; col <= 1; col++)
                        for (int row = -1; row <= 1; row++)
                        {
                            pixel = lbmp.GetPixel(x + row, y + col);
                            r += pixel.R * Gauss[Index];
                            g += pixel.G * Gauss[Index];
                            b += pixel.B * Gauss[Index];
                            Index++;
                        }
                    r /= 16;
                    g /= 16;
                    b /= 16;
                    //处理颜色值溢出
                    r = r > 255 ? 255 : r;
                    r = r < 0 ? 0 : r;
                    g = g > 255 ? 255 : g;
                    g = g < 0 ? 0 : g;
                    b = b > 255 ? 255 : b;
                    b = b < 0 ? 0 : b;
                    newlbmp.SetPixel(x - 1, y - 1, Color.FromArgb(r, g, b));
                }
            }

            lbmp.UnlockBits();
            newlbmp.UnlockBits();

            return bm;
        }

        /// <summary>
        /// 雾化效果
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="backgroundcolor"></param>
        /// <returns></returns>
        public static Bitmap ConvertToAtomization(Bitmap bitmap, Color backgroundcolor)
        {
            Bitmap tm = new Bitmap(bitmap);
            using (Graphics graphic = Graphics.FromImage(tm))
            {
                graphic.Clear(backgroundcolor);
                graphic.DrawImage(bitmap, new Rectangle(new Point(0, 0), bitmap.Size));
            }

            Bitmap bm = new Bitmap(bitmap.Width, bitmap.Height);
            Color pixel;
            System.Random rd = new Random();

            LockBitmap lbmp = new LockBitmap(tm);
            LockBitmap newlbmp = new LockBitmap(bm);
            lbmp.LockBits();
            newlbmp.LockBits();

            for (int x = 1; x < bitmap.Width - 1; x++)
            {
                for (int y = 1; y < bitmap.Height - 1; y++)
                {
                    int k = rd.Next(123456);
                    //像素块大小
                    int dx = x + k % 19;
                    int dy = y + k % 19;
                    if (dx >= bitmap.Width)
                        dx = bitmap.Width - 1;
                    if (dy >= bitmap.Height)
                        dy = bitmap.Height - 1;
                    pixel = lbmp.GetPixel(dx, dy);
                    newlbmp.SetPixel(x, y, pixel);
                }
            }

            lbmp.UnlockBits();
            newlbmp.UnlockBits();

            return bm;
        }

        /// <summary>
        /// 锐化效果
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        public static Bitmap ConvertToSharpen(Bitmap bitmap, Color backgroundcolor)
        {
            int height = bitmap.Height;
            int width = bitmap.Width;

            Bitmap tm = new Bitmap(bitmap);
            Bitmap newbmp = new Bitmap(width, height);

            using (Graphics graphic = Graphics.FromImage(tm))
            {
                graphic.Clear(backgroundcolor);
                graphic.DrawImage(bitmap, new Rectangle(new Point(0, 0), bitmap.Size));
            }

            LockBitmap lbmp = new LockBitmap(tm);
            LockBitmap newlbmp = new LockBitmap(newbmp);
            lbmp.LockBits();
            newlbmp.LockBits();

            Color pixel;
            //拉普拉斯模板
            int[] Laplacian = { -1, -1, -1, -1, 9, -1, -1, -1, -1 };
            for (int x = 1; x < width - 1; x++)
            {
                for (int y = 1; y < height - 1; y++)
                {
                    int r = 0, g = 0, b = 0;
                    int Index = 0;
                    for (int col = -1; col <= 1; col++)
                    {
                        for (int row = -1; row <= 1; row++)
                        {
                            pixel = lbmp.GetPixel(x + row, y + col); r += pixel.R * Laplacian[Index];
                            g += pixel.G * Laplacian[Index];
                            b += pixel.B * Laplacian[Index];
                            Index++;
                        }
                    }
                    //处理颜色值溢出
                    r = r > 255 ? 255 : r;
                    r = r < 0 ? 0 : r;
                    g = g > 255 ? 255 : g;
                    g = g < 0 ? 0 : g;
                    b = b > 255 ? 255 : b;
                    b = b < 0 ? 0 : b;
                    newlbmp.SetPixel(x - 1, y - 1, Color.FromArgb(r, g, b));
                }
            }
            lbmp.UnlockBits();
            newlbmp.UnlockBits();
            return newbmp;
        }

        /// <summary>
        /// 旋转图片
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="angle"></param>
        /// <returns></returns>
        public static Bitmap ConvertToRotate(Bitmap bitmap, int angle)
        {
            int width = bitmap.Width;
            int height = bitmap.Height;
            Bitmap newbmp = null;
            switch (angle)
            {
                case 90:
                    newbmp = new Bitmap(height, width);
                    using (Graphics g = Graphics.FromImage(newbmp))
                    {
                        Point[] destinationPoints = {
                        new Point(height, 0), // destination for upper-left point of original
                        new Point(height, width),// destination for upper-right point of original
                        new Point(0, 0)}; // destination for lower-left point of original
                        g.DrawImage(bitmap, destinationPoints);
                    }
                    break;
                case 180:
                    newbmp = new Bitmap(width, height);
                    using (Graphics g = Graphics.FromImage(newbmp))
                    {
                        Point[] destinationPoints = {
                        new Point(width, height), // destination for upper-left point of original
                        new Point(0, height),// destination for upper-right point of original
                        new Point(width, 0)}; // destination for lower-left point of original
                        g.DrawImage(bitmap, destinationPoints);
                    }
                    break;
                case 270:
                    newbmp = new Bitmap(height, width);
                    using (Graphics g = Graphics.FromImage(newbmp))
                    {
                        Point[] destinationPoints = {
                        new Point(width, height), // destination for upper-left point of original
                        new Point(0, height),// destination for upper-right point of original
                        new Point(width, 0)}; // destination for lower-left point of original
                        g.DrawImage(bitmap, destinationPoints);
                    }
                    break;
                case 0:
                case 360:
                    newbmp = new Bitmap(width, height);
                    using (Graphics g = Graphics.FromImage(newbmp))
                    {
                        g.DrawImage(bitmap, new Point(0, 0));
                    }
                    break;
            }
            return newbmp;
        }

        /// <summary>
        /// 图片黑白色效果
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        public static Bitmap ConvertToRGBGray(Bitmap bitmap)
        {
            int wide = bitmap.Width;
            int height = bitmap.Height;
            Rectangle rect = new Rectangle(0, 0, wide, height);
            // 将Bitmap锁定到系统内存中, 获得BitmapData
            BitmapData srcBmData = bitmap.LockBits(rect,
                      ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            //创建Bitmap
            Bitmap dstBitmap = CreateGrayscaleImage(wide, height);//这个函数在后面有定义
            BitmapData dstBmData = dstBitmap.LockBits(rect,
                      ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
            // 位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行
            System.IntPtr srcPtr = srcBmData.Scan0;
            System.IntPtr dstPtr = dstBmData.Scan0;
            // 将Bitmap对象的信息存放到byte数组中
            int src_bytes = srcBmData.Stride * height;
            byte[] srcValues = new byte[src_bytes];
            int dst_bytes = dstBmData.Stride * height;
            byte[] dstValues = new byte[dst_bytes];
            //复制GRB信息到byte数组
            System.Runtime.InteropServices.Marshal.Copy(srcPtr, srcValues, 0, src_bytes);
            System.Runtime.InteropServices.Marshal.Copy(dstPtr, dstValues, 0, dst_bytes);
            // 根据Y=0.299*R+0.114*G+0.587B,Y为亮度
            for (int i = 0; i < height; i++)
                for (int j = 0; j < wide; j++)
                {
                    //只处理每行中图像像素数据,舍弃未用空间
                    //注意位图结构中RGB按BGR的顺序存储
                    int k = 3 * j;
                    byte temp = (byte)(srcValues[i * srcBmData.Stride + k + 2] * .299
                         + srcValues[i * srcBmData.Stride + k + 1] * .587 + srcValues[i * srcBmData.Stride + k] * .114);
                    dstValues[i * dstBmData.Stride + j] = temp;
                }
            //将更改过的byte[]拷贝到原位图
            System.Runtime.InteropServices.Marshal.Copy(dstValues, 0, dstPtr, dst_bytes);

            // 解锁位图
            bitmap.UnlockBits(srcBmData);
            dstBitmap.UnlockBits(dstBmData);
            return dstBitmap;

        }

        /// <summary>
        /// 生成缩略图
        /// </summary>
        /// <param name="bitmap">原图片</param>
        /// <param name="width">长度</param>
        /// <param name="height">宽度</param>
        /// <returns></returns>
        public static Bitmap ConvertToThumbnailImage(Bitmap bitmap, int width, int height)
        {
            Bitmap bm;

            try
            {
                Image.GetThumbnailImageAbort callb = new Image.GetThumbnailImageAbort(() => { return false; });
                bm = (Bitmap)bitmap.GetThumbnailImage(width, height, callb, IntPtr.Zero);
            }
            catch
            {
                bm = new Bitmap(bitmap);
            }

            return bm;
        }

        /// <summary>
        /// 生成缩略图
        /// </summary>
        /// <param name="bitmap">原图片</param>
        /// <param name="scale">比例 如：0.5</param>
        /// <returns></returns>
        public static Bitmap ConvertToThumbnailImage(Bitmap bitmap, double scale)
        {
            int width = (int)(bitmap.Width * scale);
            int height = (int)(bitmap.Height * scale);
            return ConvertToThumbnailImage(bitmap, width, height);
        }

        /// <summary>
        /// 生成缩略图
        /// </summary>
        /// <param name="bitmap">原图片</param>
        /// <param name="width">长度</param>
        /// <param name="height">宽度</param>
        /// <param name="model">模式</param>
        /// <param name="backcolor">背景色</param>
        /// <returns></returns>
        public static Bitmap ConvertToCuttingImage(Bitmap bitmap, int width, int height,CuttingModel model,Color backcolor)
        {
            int x = 0;
            int y = 0;

            int ow = bitmap.Width;
            int oh = bitmap.Height;   

            int nw = width;
            int nh = height;          

            switch ((int)model)
            {
                case 1: 
                    nh = (int)(height * 1.0 / width * width); 
                    break;
                case 2: 
                    nw = (int)(width * 1.0 / height * height);
                    break;
                case 3:
                    if ((double)bitmap.Width / (double)bitmap.Height > (double)nw / (double)nh)
                    {
                        oh = bitmap.Height;
                        ow = bitmap.Height * nw / nh;
                        y = 0;
                        x = (bitmap.Width - ow) / 2;
                    }
                    else
                    {
                        ow = bitmap.Width;
                        oh = bitmap.Width * height / nw;
                        x = 0;
                        y = (bitmap.Height - oh) / 2;
                    } 
                    break;
            }

            //新建一个bmp图片 
            Bitmap nm = new System.Drawing.Bitmap(width, height);

            using (Graphics g = Graphics.FromImage(nm))
            {
                //设置高质量插值法 
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;

                //设置高质量,低速度呈现平滑程度 
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

                //清空画布并以透明背景色填充 
                g.Clear(backcolor);

                g.DrawImage(bitmap, new Rectangle(0, 0, nw, nh), new Rectangle(x, y, ow, oh), GraphicsUnit.Pixel);
            }

            return nm;

        }

        /// <summary>
        /// 压缩图片
        /// </summary>
        /// <param name="bitmap">原图片</param>
        /// <param name="scale">设置压缩的比例1-100 </param>
        /// <returns></returns>
        public static Bitmap ConvertToCompressImage(Bitmap bitmap, long scale,string format)
        {
            EncoderParameters ep = null;
            ImageCodecInfo info = GetEncoderInfo("image/" + format + "");

            if (info == null)
                return bitmap;

            ep = new EncoderParameters(2);
            ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionLZW);            
            ep.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long)scale);
            //ep.Param[2] = new EncoderParameter(System.Drawing.Imaging.Encoder.ColorDepth, 1L);    

            using (System.IO.Stream steam = new System.IO.MemoryStream())
            {
                bitmap.Save(steam, info, ep);
                return (Bitmap)Image.FromStream(steam);
            }                   

        }

    }

    public enum CuttingModel
    {
        w = 1,
        h = 2,
        wh = 3
    }

    public class LockBitmap
    {
        Bitmap source = null;
        IntPtr Iptr = IntPtr.Zero;
        BitmapData bitmapData = null;

        public byte[] Pixels { get; set; }
        public int Depth { get; private set; }
        public int Width { get; private set; }
        public int Height { get; private set; }

        public LockBitmap(Bitmap source)
        {
            this.source = source;
        }

        /// <summary>
        /// Lock bitmap data
        /// </summary>
        public void LockBits()
        {
            try
            {
                // Get width and height of bitmap
                Width = source.Width;
                Height = source.Height;

                // get total locked pixels count
                int PixelCount = Width * Height;

                // Create rectangle to lock
                Rectangle rect = new Rectangle(0, 0, Width, Height);

                // get source bitmap pixel format size
                Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);

                // Check if bpp (Bits Per Pixel) is 8, 24, or 32
                if (Depth != 8 && Depth != 24 && Depth != 32)
                {
                    throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
                }

                // Lock bitmap and return bitmap data
                bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
                                             source.PixelFormat);

                // create byte array to copy pixel values
                int step = Depth / 8;
                Pixels = new byte[PixelCount * step];
                Iptr = bitmapData.Scan0;

                // Copy data from pointer to array
                Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Unlock bitmap data
        /// </summary>
        public void UnlockBits()
        {
            try
            {
                // Copy data from byte array to pointer
                Marshal.Copy(Pixels, 0, Iptr, Pixels.Length);

                // Unlock bitmap data
                source.UnlockBits(bitmapData);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Get the color of the specified pixel
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public Color GetPixel(int x, int y)
        {
            Color clr = Color.Empty;

            // Get color components count
            int cCount = Depth / 8;

            // Get start index of the specified pixel
            int i = ((y * Width) + x) * cCount;

            if (i > Pixels.Length - cCount)
                throw new IndexOutOfRangeException();

            if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha
            {
                byte b = Pixels[i];
                byte g = Pixels[i + 1];
                byte r = Pixels[i + 2];
                byte a = Pixels[i + 3]; // a
                clr = Color.FromArgb(a, r, g, b);
            }
            if (Depth == 24) // For 24 bpp get Red, Green and Blue
            {
                byte b = Pixels[i];
                byte g = Pixels[i + 1];
                byte r = Pixels[i + 2];
                clr = Color.FromArgb(r, g, b);
            }
            if (Depth == 8)
            // For 8 bpp get color value (Red, Green and Blue values are the same)
            {
                byte c = Pixels[i];
                clr = Color.FromArgb(c, c, c);
            }
            return clr;
        }

        /// <summary>
        /// Set the color of the specified pixel
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="color"></param>
        public void SetPixel(int x, int y, Color color)
        {
            // Get color components count
            int cCount = Depth / 8;

            // Get start index of the specified pixel
            int i = ((y * Width) + x) * cCount;

            if (Depth == 32) // For 32 bpp set Red, Green, Blue and Alpha
            {
                Pixels[i] = color.B;
                Pixels[i + 1] = color.G;
                Pixels[i + 2] = color.R;
                Pixels[i + 3] = color.A;
            }
            if (Depth == 24) // For 24 bpp set Red, Green and Blue
            {
                Pixels[i] = color.B;
                Pixels[i + 1] = color.G;
                Pixels[i + 2] = color.R;
            }
            if (Depth == 8)
            // For 8 bpp set color value (Red, Green and Blue values are the same)
            {
                Pixels[i] = color.B;
            }
        }
    }
}
